/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package org.webos.java.explorer;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.json.JSONArray;
import org.json.JSONObject;
import org.webos.java.base.Application;
import org.webos.java.base.Dialog;
import org.webos.java.base.Frame;
import org.webos.java.base.FrameType;
import org.webos.java.base.RemoteMessage;
import org.webos.java.base.fs.FileDescriptor;
import org.webos.java.base.fs.FileManager;
import org.webos.java.base.fs.FileType;
import org.webos.java.base.tree.TreeController;
import org.webos.java.base.tree.TreeControllerItem;
import org.webos.java.base.tree.TreeControllerItemProvider;
import org.webos.java.base.tree.TreeSelectionListener;
import org.webos.java.base.workspace.FrameHolder;
import org.webos.java.base.workspace.Workspace;
import org.webos.java.explorer.ftypes.ApplicationFileDescriptor;
import org.webos.java.explorer.ftypes.ApplicationFileType;
import org.webos.java.root.Log;
import org.webos.java.base.NetworkManager;
import org.webos.java.base.fs.FileException;

/**
 *
 * @author Kostya
 */
@FrameType(code="tree-panel2", jsClass="explorer.TreePanel")
public class FileTreePanel2 extends Frame{
    private FrameHolder mainFrameHolder;
    private Explorer explorer;
    private FileViewPanel viewPanel;
    private TreeController treeController = null;
    private Workspace workspace;
    private boolean showHiddenFiles = false;
    FileTreeItem filesItem = null;

    public FileTreePanel2(final Explorer explorer, Workspace ws, FrameHolder mainFrameHolder, FileViewPanel viewPanel) {
        super(explorer, ws);
        getData().putOpt("showHidden", showHiddenFiles);
        setTitle("File browser");
        setIcon("explorer_icn explorer_icn_tree");
        this.mainFrameHolder = mainFrameHolder;
        this.explorer = explorer;
        this.viewPanel = viewPanel;
        this.workspace = ws;
        treeController = new TreeController(this);

        treeController.setItemProvider(new TreeControllerItemProvider() {

            public TreeControllerItem[] loadItems(TreeControllerItem aItem) {
                FileTreeItem item = (FileTreeItem) aItem;
                try {
                    FileDescriptor[] descs = item.getObject().getProvider().listFiles(item.getObject());
//                    TreeControllerItem[] items = new TreeControllerItem[descs.length];
                    List<FileTreeItem> items = new ArrayList<FileTreeItem>();
//                    Log.debug(this, "loadItems: "+descs.length);
                    for (int i = 0; i < descs.length; i++) {
                        FileDescriptor desc = descs[i];
                        if(!desc.getName().startsWith(".") || showHiddenFiles)
                            items.add(new FileTreeItem(treeController, desc));
                    }
                    Collections.sort(items, new Comparator<FileTreeItem>() {
                        public int compare(FileTreeItem o1, FileTreeItem o2) {
                            return o1.getObject().getName().compareToIgnoreCase(o2.getObject().getName());
                        }
                    });
                    return items.toArray(new FileTreeItem[0]);
                } catch (Throwable t) {
                    if(t instanceof FileException){
                        explorer.sendError(55, t.getMessage());
                    }
                    Log.error(this, "Exception thrown: " + t.getMessage());
                    t.printStackTrace();
                }
                return new TreeControllerItem[0];
            }
        });

        filesItem = new FileTreeItem(treeController, explorer.getSession().getRootProvider().getRootFile());
        treeController.getRoot().insertChild(0, filesItem);
        FileTreeItem appsItem = new FileTreeItem(treeController, explorer.getSession().getApplicationProvider().getRootFile());
        treeController.getRoot().insertChild(1, appsItem);
        treeController.setElementState(filesItem, false);
        treeController.setElementState(appsItem, false);
        treeController.addTreeSelectionListener(new TreeSelectionListener() {

            public void selectionChanged(TreeControllerItem aItem) {
                panelSelectionChanged(aItem);
            }
        });
    }

    public void panelSelectionChanged(TreeControllerItem aItem) {
//        Log.debug(this, "selected: "+aItem);
        viewPanel.displayFile((FileDescriptor) (aItem == null ? null : aItem.getObject()), null);
    }

    @RemoteMessage
    public JSONObject changeShowHidden(JSONObject aData){
        try {
            showHiddenFiles = !showHiddenFiles;
            getData().put("showHidden", showHiddenFiles);
            treeController.setElementState(filesItem, true);
            treeController.setElementState(filesItem, false);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    @RemoteMessage
    public JSONObject getTree(JSONObject aData){
        return treeController.getTree();
    }

    @RemoteMessage
    public JSONObject loadItems(JSONObject aData){
        try {
            JSONObject obj = treeController.loadItems(aData.getString("id"));
//            Log.debug(this, "loadItems: "+obj);
            return obj;
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    @RemoteMessage
    public JSONObject selectionChanged(JSONObject aData){
        try {
            treeController.selectionChanged(aData.getString("id"));
            if(treeController.getSelected()!=null){
                FileDescriptor desc = (FileDescriptor) treeController.getSelected().getObject();
                JSONArray arr = new JSONArray();
                for (FileType fType : desc.getAllowedFileTypes()) {
                    if(fType.equals(FileManager.PLAIN_FILE_TYPE)){
                        for (String string : FileManager.getPlainFileTypes()) {
                            JSONObject obj = new JSONObject();
                            obj.put("code", string);
                            obj.put("name", FileManager.getFileType(string).getName());
                            obj.put("icon", FileManager.getFileType(string).getIcon());
                            arr.put(obj);
                        }

                    }else{
                        JSONObject obj = new JSONObject();
                        obj.put("code", fType.getCode());
                        obj.put("name", fType.getName());
                        obj.put("icon", fType.getIcon());
                        arr.put(obj);
                    }
                }
                JSONObject obj = new JSONObject();
                obj.put("file_types", arr);
                Log.debug(this, obj);
                return obj;
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    @RemoteMessage
    public void setState(JSONObject aData){
        try {
            treeController.setState(aData.getString("id"), aData.getBoolean("collapsed"));
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
    }

    private void showEditor(Object editor){
        if(editor instanceof Frame){
            Frame frame = (Frame) editor;
            frame.setCloseable(true);
            frame.setDynamic(true);
            frame.setFrameHolder(mainFrameHolder);
            frame.setWorkspace(getParentWorkspace());
            explorer.addFrame(frame);
            explorer.showFrame(frame);
        }
        if(editor instanceof Dialog){
            Dialog dialog = (Dialog) editor;
            explorer.getSession().addDialog(dialog);
        }
    }

    @RemoteMessage
    public JSONObject elementDblClick(JSONObject data){
        try {
            FileTreeItem item = (FileTreeItem) treeController.findItemByID(data.getString("id"), treeController.getRoot());
            if(item!=null && (item.getObject() instanceof ApplicationFileDescriptor)){
                ApplicationFileDescriptor desc = (ApplicationFileDescriptor) item.getObject();
                ApplicationFileType aft = (ApplicationFileType) FileManager.getFileType(item.getObject().getType());
                Application startApplication = aft.startApplication(desc, explorer.getSession());
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    @RemoteMessage
    public JSONObject newFile(JSONObject data){
        try {
            if(treeController.getSelected()==null)
                throw new Exception("selected is null");
            FileType fileType = FileManager.getFileType(data.optString("type"));
            if(fileType==null)
                throw new Exception("fileType is null");
            //Also check this fileType in selected
            FileDescriptor newFile = new FileDescriptor();
            FileDescriptor selected = (FileDescriptor) treeController.getSelected().getObject();
            newFile.setParentFile(selected);
            newFile.setProvider(selected.getProvider());
            newFile.setType(data.optString("type"));
            Object editor = fileType.getEditor(newFile, explorer);
            if(editor==null)
                throw new Exception("editor is null");
            showEditor(editor);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
//        Log.error(this, "That's strange...");
        return null;
    }

    JSONObject removeFile(FileDescriptor aFile) {
        try {
            if(!aFile.isRemovable()){
                return NetworkManager.getErrorObject(6, "Can't remove unremovable file");
            }
            if(!aFile.getProvider().removeFile(aFile, true)){
                return NetworkManager.getErrorObject(7, "Error removing file. File is not removed");
            }
            FileTreeItem item = (FileTreeItem) treeController.findItemByID(aFile.getId(), treeController.getRoot());
            if(item!=null){
                if(!item.remove()){
                }
            }
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    public JSONObject editFile(FileDescriptor aFile, FileType aType){
        try {
            if(aType==null)
                return NetworkManager.getErrorObject(5, "Unknown file type");
            Object editor = aType.getEditor(aFile, explorer);
            if(editor==null){
                return NetworkManager.getErrorObject(5, "Unknown file type");
            }
            showEditor(editor);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    @RemoteMessage
    public JSONObject openNewViewer(JSONObject aData){
        try {
            FileTreeItem item = (FileTreeItem) treeController.findItemByID(aData.optString("id"), treeController.getRoot());
            if(item==null)
                return NetworkManager.getErrorObject(7, "Can't find file");
            FileViewPanel newPanel = new FileViewPanel(explorer, workspace);
            newPanel.setCloseable(true);
            newPanel.setDynamic(true);
            newPanel.setTreePanel(this);
            newPanel.setFrameHolder(mainFrameHolder);
            newPanel.setWorkspace(getParentWorkspace());
            explorer.addFrame(newPanel);
            explorer.showFrame(newPanel);
            newPanel.displayFile(item.getObject(), null);
        } catch (Throwable t) {
            Log.error(this, "Exception thrown: " + t.getMessage());
            t.printStackTrace();
        }
        return null;
    }

    public void fileUpdated(FileDescriptor aDescriptor, boolean aNewFile, boolean aFullReload){
        if(aNewFile){
            FileTreeItem item = (FileTreeItem) treeController.findItemByID(
                    aDescriptor.getParentFile().getId(), treeController.getRoot());
            if(item!=null){
//                Log.debug(this, "fileUpdated, state: "+item.isCollapsed());
                if(item.isCollapsed()){
                    treeController.setElementState(item, false);
                }else{
                    FileTreeItem fti = new FileTreeItem(treeController, aDescriptor);
                    item.insertChild(item.getChildrenCount(), fti);
                    if(aFullReload)
                        treeController.setElementState(fti, false);
                }
            }
        }else{
            FileTreeItem item = (FileTreeItem) treeController.findItemByID(
                    aDescriptor.getId(), treeController.getRoot());
            if(item!=null){
                item.setObject(aDescriptor);
//                Log.debug(this, "Modified item: "+aDescriptor.getName()+", "+item.getObject().getName(), aDescriptor, item.getObject());
                item.notifyModified();
                if(aFullReload){
                    if(!item.isCollapsed())
                        treeController.setElementState(item, true);
                    treeController.setElementState(item, false);
                }
            }
        }
    }
}
